
/**
  ******************************************************************************
  * Copyright 2021 The grapilot Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       gp_socket_udp.c
  * @author     baiyang
  * @date       2021-10-1
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "gp_socket_udp.h"

#include <tchar.h>
#include <stdio.h>
/*-----------------------------------macro------------------------------------*/

/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/

/*----------------------------------variable----------------------------------*/

/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
void socket_udp_ctor(gp_socket_udp *udp)
{
    memset(udp->m_SrcIP, 0, sizeof(udp->m_SrcIP));
    memset(udp->m_DesIP, 0, sizeof(udp->m_DesIP));

    udp->m_uDesSendPort = 0;
    udp->m_uSrcSendPort = 0;
    udp->m_uSrcRecvPort = 0;

    udp->m_sockStream = INVALID_SOCKET;

    memset(&udp->m_SendAddr, 0, sizeof(struct sockaddr_in));
    memset(&udp->m_RecvAddr, 0, sizeof(struct sockaddr_in));

    udp->connect = false;
}

//lpDesIP：目标IP地址  uSrcRecvPort：监听端口号  uDesRecvPort：目标端口号  lpSrcIP：源IP地址  源端口号
// lpDesIP: 目标IP地址，uSrcRecvPort：监听端口（接收），uDesSendPort：目标端口号（发送），lpSrcIP：源IP地址，uSrcSendPort：源端口号
bool socket_udp_init(gp_socket_udp *udp, const char* lpDesIP, uint32_t uSrcRecvPort, uint32_t uDesSendPort, const char* lpSrcIP, uint32_t uSrcSendPort)
{
    socket_udp_ctor(udp);

    //IP地址赋值
    if (lpDesIP != NULL)
    {
        _tcscpy(udp->m_DesIP,lpDesIP);
    }

    if (lpSrcIP != NULL)
    {
        _tcscpy(udp->m_SrcIP, lpSrcIP);
    }

    //设置端口号
    udp->m_uSrcRecvPort = uSrcRecvPort;
    udp->m_uDesSendPort = uDesSendPort;
    udp->m_uSrcSendPort = uSrcSendPort;

    if (udp->m_uDesSendPort != 0)
    {
        if (_tcscmp(udp->m_DesIP, _T("")) == 0)
        {
            socket_udp_close(udp);
            return FALSE;
        }

        udp->m_sockStream = socket(AF_INET, SOCK_DGRAM, 0);
        if (udp->m_sockStream == INVALID_SOCKET)
        {
            //socket创建失败
            socket_udp_close(udp);
            return FALSE;
        }

        udp->m_SendAddr.sin_family = AF_INET;
        udp->m_SendAddr.sin_port = htons(udp->m_uDesSendPort);
        InetPton(AF_INET, udp->m_DesIP, &udp->m_SendAddr.sin_addr);

        udp->m_RecvAddr.sin_family = AF_INET;
        udp->m_RecvAddr.sin_port = htons(udp->m_uDesSendPort);
        InetPton(AF_INET, udp->m_SrcIP, &udp->m_RecvAddr.sin_addr);

        if (udp->m_uSrcSendPort != 0)
        {
            //IP地址为零
            if (_tcscmp(udp->m_SrcIP, _T("")) == 0)
            {
                SOCKADDR_IN local_send;
                local_send.sin_family = AF_INET;
                local_send.sin_port = htons(udp->m_uSrcRecvPort);
                local_send.sin_addr.s_addr = htonl(INADDR_ANY);
                if (bind(udp->m_sockStream, (LPSOCKADDR)&local_send, sizeof(local_send)) == SOCKET_ERROR)
                {
                    socket_udp_close(udp);
                    return FALSE;
                }
            }
            else
            {
                SOCKADDR_IN local_send;
                local_send.sin_family = AF_INET;
                local_send.sin_port = htons(udp->m_uSrcRecvPort);
                InetPton(AF_INET, udp->m_SrcIP, &local_send.sin_addr);
                if (bind(udp->m_sockStream, (struct sockaddr*)&local_send, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
                {
                    socket_udp_close(udp);
                    return FALSE;
                }
            }
        }else {
            if (connect(udp->m_sockStream, (SOCKADDR*)&udp->m_SendAddr, sizeof(SOCKADDR)) == SOCKET_ERROR)
            {
                udp->connect = false;
                printf("connect error !");
                socket_udp_close(udp);
                return false;
            }
    }

        struct timeval tv = { 1, 0 };
        setsockopt(udp->m_sockStream, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(struct timeval));
    }

    udp->connect = true;

    return TRUE;
}

int socket_udp_send(gp_socket_udp *udp, char* pBuf, int nSize)
{
    if (udp->m_sockStream != INVALID_SOCKET)
    {
        return sendto(udp->m_sockStream, pBuf, nSize, 0, (SOCKADDR *)&udp->m_SendAddr, sizeof(udp->m_SendAddr));
    }
    return 0;
}

int socket_udp_recv(gp_socket_udp *udp, char* pBuf, int nSize)
{
    if (udp->m_sockStream != INVALID_SOCKET)
    {
        int nRecvAddrSize = sizeof(udp->m_RecvAddr);
        return recvfrom(udp->m_sockStream, pBuf, nSize,0, (SOCKADDR *)&udp->m_RecvAddr, &nRecvAddrSize);
    }
    return 0;
}

bool socket_udp_close(gp_socket_udp *udp)
{
    if (udp->m_sockStream != INVALID_SOCKET)
    {
        shutdown(udp->m_sockStream, SD_BOTH);
        closesocket(udp->m_sockStream);
    }

    memset(udp->m_SrcIP, 0, sizeof(udp->m_SrcIP));
    memset(udp->m_DesIP, 0, sizeof(udp->m_DesIP));

    udp->m_uDesSendPort = 0;
    udp->m_uSrcSendPort = 0;
    udp->m_uSrcRecvPort = 0;

    udp->m_sockStream = INVALID_SOCKET;

    memset(&udp->m_SendAddr, 0, sizeof(udp->m_SendAddr));
    memset(&udp->m_RecvAddr, 0, sizeof(udp->m_RecvAddr));

    udp->connect = false;

    return TRUE;
}
/*------------------------------------test------------------------------------*/


